package cn.bitkit.mybatis;

import cn.bitkit.base.annotation.EntityInfo;
import cn.bitkit.base.annotation.FieldInfo;
import cn.bitkit.mybatis.annotation.Update;
import cn.bitkit.mybatis.entity.EntityClass;
import cn.bitkit.mybatis.entity.EntityField;
import cn.bitkit.base.util.ReflectUtil;
import cn.bitkit.base.util.StringUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author changbo
 */
@Slf4j
public class EntityInfoHandler {
	
	private static final Map<Class<?>, EntityClass> ENTITY_INFO_MAP = new ConcurrentHashMap<>();

	public static EntityClass getEntityInfo(Class<?> clazz) {
		EntityClass info = ENTITY_INFO_MAP.get(clazz);
		if(info == null) {
			initEntityInfo(clazz);
		}
		return info;
	}

	private static synchronized void initEntityInfo(Class<?> clazz) {
		if(ENTITY_INFO_MAP.get(clazz) == null) {
			EntityClass info = analysisEntityInfo(clazz);
			ENTITY_INFO_MAP.put(clazz, info);
		}
	}
	
	private static EntityClass analysisEntityInfo(Class<?> clazz) {
		EntityClass entityClass = new EntityClass();
		entityClass.setEntityClass(clazz);
		entityClass.setTableName(getTableName(clazz));
		if(entityClass.getTableName() == null){
			return entityClass;
		}
		entityClass.setFieldList(getTableFieldInfo(clazz));
		entityClass.setColumnList(entityClass.getFieldList().stream()
				.map(EntityField::getColumn)
				.collect(Collectors.toList()));
		entityClass.setId(entityClass.getFieldList().stream()
				.filter(EntityField::getId)
				.findFirst().orElse(null));
		//设置主键字段
		if(entityClass.getId() == null){
			EntityField defaultId = entityClass.getFieldList().getFirst();
			defaultId.setId(true);
			entityClass.setId(defaultId);
			log.error("[{}]primary key not found. will use first field[{}]", entityClass.getTableName(), defaultId.getField());
		}
		//所有字段
		entityClass.setSqlColumns(entityClass.getFieldList().stream()
				.map(EntityField::getColumn)
				.collect(Collectors.joining(",")));
		//所有字段取值
		entityClass.setSqlValues(entityClass.getFieldList().stream()
				.map(e -> "#{"+e.getField()+"}")
				.collect(Collectors.joining(",")));
		//修改所有值的字段
		entityClass.setSqlUpdate(entityClass.getFieldList().stream()
				.filter(e -> !e.getId())
				.map(e -> e.getColumn()+" = #{entity."+e.getField()+"}")
				.collect(Collectors.joining(",\n")));
		return entityClass;
	}

	private static String getTableName(Class<?> clazz) {
		EntityInfo entityInfo = clazz.getAnnotation(EntityInfo.class);
		if(entityInfo == null) {
			return null;
		}
		if(StringUtil.hasValue(entityInfo.table())){
			return entityInfo.table();
		}else{
			return StringUtil.camelToUnderscore(clazz.getSimpleName());
		}
	}
	
	private static List<EntityField> getTableFieldInfo(Class<?> clazz) {
		List<EntityField> list = new ArrayList<>();
		List<Field> fieldList = ReflectUtil.getFields(clazz);
		for(Field field : fieldList) {
			EntityField entityField = new EntityField();
			entityField.setField(field.getName());
			entityField.setColumn(StringUtil.camelToUnderscore(field.getName()));
			FieldInfo fieldInfo = field.getAnnotation(FieldInfo.class);
			initByEntityTag(entityField, field, fieldInfo);
			Update update = field.getAnnotation(Update.class);
			initByUpdateTag(entityField, field, update);
			list.add(entityField);
		}
		return list;
	}

	private static void initByEntityTag(EntityField entityField, Field field, FieldInfo fieldInfo){
		if(fieldInfo == null) {
			entityField.setColumn(StringUtil.camelToUnderscore(field.getName()));
			return;
		}
		if(fieldInfo.ignore()) {
			return;
		}
		if(StringUtil.hasValue(fieldInfo.column())){
			entityField.setColumn(fieldInfo.column());
		}else{
			entityField.setColumn(StringUtil.camelToUnderscore(field.getName()));
		}
		if(fieldInfo.id()){
			entityField.setId(true);
		}
	}

	private static void initByUpdateTag(EntityField entityField, Field field, Update update){
		if(update == null) {
			return;
		}
		entityField.setGroupList(Arrays.asList(update.group()));
	}

}
